#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===============================================================================
# Copyright 2011 zod.yslin
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 
# Author: zod.yslin
# Email: 
# File Name: iterators.py
# Description: 
#   1. The for statement calls iter() on the container object. 
#      The function returns an iterator object that defines the method next() which 
#      accesses elements in the container one at a time. 
#      When there are no more elements, next() raises a StopIteration exception 
#      which tells the for loop to terminate. 
#
#   2. generators.py use yield to automatically generate iterator.
# 
#   python3 : __next__
#   python2.x: next()
# Edit History: 
#   2011-08-09    File created.
#===============================================================================
for element in [1, 2, 3]:
    print(element)
for element in (1, 2, 3):
    print(element)
for key in {'one':1, 'two':2}:
    print(key)
for char in "123":
    print(char)
#for line in open("iterators.py"):
#    print line,

s = 'abc'
it = iter(s)
print(it)
print(it.next())
print(it.next())
print(it.next())
#it.next()  # raises a StopIteration exception which tells the for loop to terminate.

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    """
    Add iterator behavior to your classes. 
    Define an __iter__() method which returns an object with a next() method. 
    If the class defines next(), then __iter__() can just return self.
    """
    def __iter__(self):
        print("__iter__")
        return self
    def next(self):
        print("next")
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

rev = Reverse('spam')
print(iter(rev))
# The for statement calls iter() on the container object.
for char in rev:
    print(char)

class Fibs:
    def __init__(self):
        self.i = 0
        self.j = 1
     
    def __str__(self):
        return str(self.i)
     
    def __iter__(self):
        return self
     
    def next(self):
        self.i, self.j = self.j, self.i + self.j
        return self.i
 
fibs = Fibs()
for f in fibs:
    print(f)
    if f > 10000:
        break
